# @Author : Labyrinthine Leo
# @Time   : 2020.11.24
# @problem: ZDT4
# Benchmark MOP proposed by Zitzler, Deb, and Thiele
################################## Reference ################################
# E. Zitzler, K. Deb, and L. Thiele, Comparison of multiobjective           #
# evolutionary algorithms: Empirical results, Evolutionary computation,     #
# 2000, 8(2): 173-195.                                                      #
#############################################################################

import numpy as np
import platgo as pg


class ZDT4(pg.Problem):

    def __init__(self, D: int = 10) -> None:
        self.name = "ZDT4"
        self.type['multi'], self.type['real'], self.type['large'], self.type['expensive'] = [True] * 4
        self.M = 2
        self.D = D
        lb = np.hstack(([0], [-5]*(self.D - 1)))
        ub = np.hstack(([1], [5]*(self.D - 1)))
        self.borders = np.array([lb, ub])
        super().__init__()

    def cal_obj(self, pop: pg.Population) -> None:
        decs = pop.decs
        pop.cv = np.zeros((pop.N, self.D))
        g = 1 + 10*(self.D - 1) + np.sum(np.square(decs[:, 1:]) - 10*np.cos(4*np.pi*decs[:, 1:]), axis=1, keepdims=True)
        h = 1 - np.sqrt(decs[:, 0:1]/g)
        pop.objv = np.hstack((decs[:, 0:1], g*h))

    def get_optimal(self, N: int = 100) -> np.ndarray:
        # 参考点采样
        optimal1 = np.linspace(0, 1, N)
        optimal2 = 1 - optimal1 ** 0.5
        optimal = np.vstack((optimal1, optimal2)).T
        return optimal


if __name__ == '__main__':
    z = ZDT4()
    pop = pg.Population(decs=np.random.uniform(z.borders[0], z.borders[1], (10,10)))
    print(z.borders)
    print(pop.decs)
    z.cal_obj(pop)
    print(pop.objv)
    print(z.get_optimal(10))
